var path = require("path");
var through = require("through2");
var Readable = require("readable-stream");
var del = require("del");
var gulp = require("gulp");
var changed = require("gulp-changed");
var rename = require("gulp-rename");
var manifest = require("gulp-manifest");
var less = require("gulp-less");
var autoprefixer = require("gulp-autoprefixer");
var minify = require("gulp-minify-css");
var uglify = require("gulp-uglify");
var connect = require("gulp-connect");
var browserify = require("browserify");
var babelify = require("babelify");
var moment = require("moment");

var arrayStream = function(items) {
    var index = 0;
    var readable = new Readable({objectMode: true});
    readable._read = function() {
        if (index < items.length) {
            readable.push(items[index++]);
        } else {
            readable.push(null);
        }
    };
    return readable;
};

var gulpBrowserify = function(options) {
    options = options || {};
    return through.obj(function(file, encoding, callback) {
        if (file.isNull()) {
            return callback(null, file);
        } else if (file.isStream()) {
            this.emit("error", "Stream not supported");
            return callback(null, file);
        }
        options.entries = arrayStream([file.contents]);
        options.basedir = path.dirname(file.path);
        if (!("builtins" in options) && "nobuiltins" in options) {
            var builtins = require("browserify/lib/builtins");
            options.builtins = {};
            for (var x in builtins) {
                if (builtins.hasOwnProperty(x)) {
                    options.builtins[x] = builtins[x];
                }
            }
            var nobuiltins = options.nobuiltins;
            if (Object.prototype.toString.call(nobuiltins) === "[object String]") {
                nobuiltins = nobuiltins.split(" ");
            }
            nobuiltins.forEach(function(x) {
                delete options.builtins[x];
            });
        }
        var bundler = browserify(options);
        ["exclude", "add", "external", "transform", "ignore", "require"].forEach(function(method) {
            if (method in options) {
                Array.prototype.forEach.call(options[method], function(args) {
                    bundler[method].apply(bundler, [].concat(args));
                });
            }
        });
        bundler.bundle(function(error, source) {
            if (error) {
                this.emit("error", error);
            } else {
                file.contents = new Buffer(source);
                this.push(file);
            }
            callback(null, file);
        }.bind(this));
    });
};

gulp.task("copy", function() {
    return gulp.src(["src/**/*", "!**/*.less", "!**/*.babel.js", "!**/*.manifest"])
        .pipe(changed("build"))
        .pipe(gulp.dest("build"));
});

gulp.task("less", function() {
    return gulp.src("src/**/*.less")
        .pipe(rename(function(path) {
            path.extname = ".css";
        }))
        .pipe(changed("build"))
        .pipe(less())
        .pipe(autoprefixer())
        .pipe(minify())
        .pipe(gulp.dest("build"));
});

gulp.task("babel", function() {
    return gulp.src("src/**/*.babel.js")
        .pipe(rename(function(path) {
            path.basename = path.basename.replace(/\.babel$/, "");
        }))
        .pipe(changed("build"))
        .pipe(gulpBrowserify({
            ignore: "source-map-support/register",
            transform: babelify.configure({stage: 0})
        }))
        .pipe(uglify())
        .pipe(gulp.dest("build"));
});

gulp.task("manifest", ["copy", "less", "babel"], function() {
    return gulp.src("build/**/*")
        .pipe(manifest({
            hash: true,
            timestamp: false,
            filename: "cache.manifest",
            exclude: "cache.manifest"
        }))
        .pipe(gulp.dest("build"));
});

gulp.task("clean", function(callback) {
    del(["build"], callback);
});

gulp.task("connect", function() {
    connect.server({
        root: "build",
        port: 8000
    });
});

gulp.task("default", ["manifest"]);
